package luj.groovy.excel.internal.table.header

import groovy.transform.PackageScope
import org.apache.poi.ss.usermodel.Cell
import org.apache.poi.ss.usermodel.CellType
import org.apache.poi.ss.usermodel.Row
import org.apache.poi.ss.usermodel.Sheet
import org.apache.poi.ss.util.CellRangeAddress

import java.util.stream.Collectors
import java.util.stream.IntStream

@PackageScope
class HeaderFieldFinder {

  interface Result {

    FieldImpl field()

    int firstColumn()

    int lastColumn()
  }

  HeaderFieldFinder(Sheet sheet, int startRow, int endRow, List<CellRangeAddress> mergedList) {
    _sheet = sheet
    _startRow = startRow
    _endRow = endRow
    _mergedList = mergedList
  }

  List<Result> findFields() {
    def result = []
    def posMap = new FieldPosMap(_mergedList)

    (_startRow..<_endRow).each {
      Row row = _sheet.getRow(it)
      result += findInRow(row, posMap)
    }

    return result
  }

  private List<Result> findInRow(Row row, FieldPosMap posMap) {
    return IntStream.range(row.firstCellNum, row.lastCellNum)
        .mapToObj() { row.getCell(it) }
        .filter { it != null && it.cellType != CellType.BLANK }
        .map { makeField(it, posMap) }
        .filter { it != null }
        .collect(Collectors.toList())
  }

  private Result makeField(Cell cell, FieldPosMap posMap) {
    String fieldName = cell.stringCellValue
    if (!fieldName) {
      return null
    }

    def field = new FieldImpl()
    field._name = fieldName
    field._childList = []

    def (int firstCol, int lastCol) = resolveWidth(cell)
    field._index = firstCol

    FieldImpl parent = findParent(cell, posMap)
    if (parent) {
      field._parent = parent
      parent._childList.add(field)
    }

    posMap.add(cell, field)

    return new Result() {
      @Override
      FieldImpl field() {
        return field
      }

      @Override
      int firstColumn() {
        return firstCol
      }

      @Override
      int lastColumn() {
        return lastCol
      }
    }
  }

  private List<Integer> resolveWidth(Cell cell) {
    int col = cell.columnIndex

    return _mergedList.stream()
        .filter { it.isInRange(cell.rowIndex, col) }
        .findAny()
        .map { [it.firstColumn, it.lastColumn] }
        .orElse([col, col])
  }

  private FieldImpl findParent(Cell cell, FieldPosMap posMap) {
    int parentRow = cell.rowIndex - 1
    if (parentRow < _startRow) {
      return null
    }
    return posMap.get(parentRow, cell.columnIndex).field()
  }

  private final Sheet _sheet

  private final int _startRow
  private final int _endRow

  private final List<CellRangeAddress> _mergedList
}
